#pragma once

#include <iostream>
#include <string>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "../common/util.hpp"
#include "../common/log.hpp"

namespace ns_compiler
{
    using namespace ns_util;
    using namespace ns_log;

    class Compiler
    {
    public:
    
        Compiler()=default;
        ~Compiler()=default;

        static bool Compile(const std::string &file_name)
        {
            // 创建子进程
            pid_t pid=fork();
            if(pid<0)
            {
                LOG(LOGLEVEL::ERROR)<<"内部错误，创建子进程失败\n";
                return false;
            }

            if(0==pid)
            {
                umask(0);

                // 根据file_name创建标准错误文件并以写方式打开
                int compile_error_fd=open(PathUtil::CompileError(file_name).c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
                if(compile_error_fd<0)
                {
                    LOG(LOGLEVEL::WARNING) << "没有成功形成stderr文件\n";
                    exit(1);
                }

                // 进行重定向, 重定向标准错误到stderr_fd
                if(dup2(compile_error_fd,2)<0)
                {
                    LOG(LOGLEVEL::WARNING)<<"重定向失败\n";
                    exit(2);
                }

                // 进行进程替换,让g++生成可执行程序, 程序替换, 并不影响进程的文件描述符表
                execlp("g++","g++","-o",PathUtil::Exe(file_name).c_str(),\
                        PathUtil::Src(file_name).c_str(),"-std=c++11",nullptr);

                LOG(LOGLEVEL::ERROR) << "启动编译器g++失败，可能是参数错误\n";
                exit(3);  
            }
            else if(pid>0)
            {
                // 等待子进程
                waitpid(pid,nullptr,0);
                // 编译有没有成功, 就看有没有生成可执行程序
                if(FileUtil::IsFileExist(PathUtil::Exe(file_name)))
                {
                    LOG(LOGLEVEL::INFO) << PathUtil::Src(file_name) << " 编译成功!\n";
                    return true;
                }
            }

            LOG(LOGLEVEL::ERROR) << "编译失败，没有形成可执行程序\n";
            return false;
        }

    };
}